home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Software Money Savers / VirtualDub / Source / VirtualDub-1.7.7-src.7z / src / Riza / audioout.cpp next >
Encoding:
C/C++ Source or Header  |  2006-12-01  |  14.8 KB  |  665 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #define INITGUID
  19. #include <windows.h>
  20. #include <mmsystem.h>
  21. #include <cguid.h>
  22. #include <dsound.h>
  23.  
  24. #include <vd2/Riza/audioout.h>
  25.  
  26. extern HINSTANCE g_hInst;
  27.  
  28. class VDAudioOutputWaveOutW32 : public IVDAudioOutput {
  29. public:
  30.     VDAudioOutputWaveOutW32();
  31.     ~VDAudioOutputWaveOutW32();
  32.  
  33.     bool    Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf);
  34.     void    Shutdown();
  35.     void    GoSilent();
  36.  
  37.     bool    IsSilent();
  38.     bool    IsFrozen();
  39.     uint32    GetAvailSpace();
  40.     sint32    GetPosition();
  41.  
  42.     bool    Start();
  43.     bool    Stop();
  44.     bool    Flush();
  45.  
  46.     bool    Write(const void *data, uint32 len);
  47.     bool    Finalize(uint32 timeout);
  48.  
  49. private:
  50.     bool    CheckBuffers();
  51.     bool    WaitBuffers(uint32 timeout);
  52.  
  53.     uint32    mBlockHead;
  54.     uint32    mBlockTail;
  55.     uint32    mBlockWriteOffset;
  56.     uint32    mBlocksPending;
  57.     uint32    mBlockSize;
  58.     uint32    mBlockCount;
  59.     vdblock<char> mBuffer;
  60.     vdblock<WAVEHDR> mHeaders;
  61.  
  62.     HWAVEOUT__ *mhWaveOut;
  63.     void *    mhWaveEvent;
  64.     uint32    mSamplesPerSec;
  65.     uint32    mAvgBytesPerSec;
  66.     VDCriticalSection    mcsWaveDevice;
  67.  
  68.     enum InitState {
  69.         kStateNone        = 0,
  70.         kStateOpened    = 1,
  71.         kStatePlaying    = 2,
  72.         kStateSilent    = 10,
  73.     } mCurState;
  74. };
  75.  
  76. IVDAudioOutput *VDCreateAudioOutputWaveOutW32() {
  77.     return new VDAudioOutputWaveOutW32;
  78. }
  79.  
  80. VDAudioOutputWaveOutW32::VDAudioOutputWaveOutW32()
  81.     : mBlockHead(0)
  82.     , mBlockTail(0)
  83.     , mBlockWriteOffset(0)
  84.     , mBlocksPending(0)
  85.     , mBlockSize(0)
  86.     , mBlockCount(0)
  87.     , mhWaveOut(NULL)
  88.     , mhWaveEvent(NULL)
  89.     , mSamplesPerSec(0)
  90.     , mAvgBytesPerSec(0)
  91.     , mCurState(kStateNone)
  92. {
  93. }
  94.  
  95. VDAudioOutputWaveOutW32::~VDAudioOutputWaveOutW32() {
  96.     Shutdown();
  97. }
  98.  
  99. bool VDAudioOutputWaveOutW32::Init(uint32 bufsize, uint32 bufcount, const WAVEFORMATEX *wf) {
  100.     mBuffer.resize(bufsize * bufcount);
  101.     mBlockHead = 0;
  102.     mBlockTail = 0;
  103.     mBlockWriteOffset = 0;
  104.     mBlocksPending = 0;
  105.     mBlockSize = bufsize;
  106.     mBlockCount = bufcount;
  107.  
  108.     if (!mhWaveEvent) {
  109.         mhWaveEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  110.  
  111.         if (!mhWaveEvent)
  112.             return false;
  113.     }
  114.  
  115.     MMRESULT res = waveOutOpen(&mhWaveOut, WAVE_MAPPER, wf, (DWORD_PTR)mhWaveEvent, 0, CALLBACK_EVENT);
  116.     if (MMSYSERR_NOERROR != res) {
  117.         Shutdown();
  118.         return false;
  119.     }
  120.  
  121.     mCurState = kStateOpened;
  122.     mSamplesPerSec = wf->nSamplesPerSec;
  123.     mAvgBytesPerSec = wf->nAvgBytesPerSec;
  124.  
  125.     // Hmmm... we can't allocate buffers while the wave device
  126.     // is active...
  127.     mHeaders.resize(bufcount);
  128.     memset(mHeaders.data(), 0, bufcount * sizeof mHeaders[0]);
  129.  
  130.     for(uint32 i=0; i<bufcount; ++i) {
  131.         WAVEHDR& hdr = mHeaders[i];
  132.  
  133.         hdr.dwBufferLength    = bufsize;
  134.         hdr.dwBytesRecorded    = 0;
  135.         hdr.dwFlags            = 0;
  136.         hdr.dwLoops            = 0;
  137.         hdr.dwUser            = 0;
  138.         hdr.lpData            = mBuffer.data() + bufsize * i;
  139.  
  140.         res = waveOutPrepareHeader(mhWaveOut, &hdr, sizeof hdr);
  141.         if (MMSYSERR_NOERROR != res) {
  142.             Shutdown();
  143.             return false;
  144.         }
  145.     }
  146.  
  147.     waveOutPause(mhWaveOut);
  148.     return true;
  149. }
  150.  
  151. void VDAudioOutputWaveOutW32::Shutdown() {
  152.     if (mCurState == kStateSilent)
  153.         return;
  154.  
  155.     Stop();
  156.  
  157.     if (!mHeaders.empty()) {
  158.         for(int i=mHeaders.size()-1; i>=0; --i) {
  159.             WAVEHDR& hdr = mHeaders[i];
  160.  
  161.             if (hdr.dwFlags & WHDR_PREPARED)
  162.                 waveOutUnprepareHeader(mhWaveOut, &hdr, sizeof hdr);
  163.         }
  164.     }
  165.  
  166.     mHeaders.clear();
  167.     mBuffer.clear();
  168.     mBlocksPending = 0;
  169.     mBlockCount = 0;
  170.     mBlockSize = 0;
  171.  
  172.     if (mhWaveOut) {
  173.         waveOutClose(mhWaveOut);
  174.         mhWaveOut = NULL;
  175.     }
  176.  
  177.     if (mhWaveEvent) {
  178.         CloseHandle(mhWaveEvent);
  179.         mhWaveEvent = NULL;
  180.     }
  181.  
  182.     mCurState = kStateNone;
  183. }
  184.  
  185. void VDAudioOutputWaveOutW32::GoSilent() {
  186.     mCurState = kStateSilent;
  187. }
  188.  
  189. bool VDAudioOutputWaveOutW32::IsSilent() {
  190.     return mCurState == kStateSilent;
  191. }
  192.  
  193. bool VDAudioOutputWaveOutW32::Start() {
  194.     if (mCurState == kStateSilent)
  195.         return true;
  196.  
  197.     if (mCurState < kStateOpened)
  198.         return false;
  199.  
  200.     if (MMSYSERR_NOERROR != waveOutRestart(mhWaveOut))
  201.         return false;
  202.  
  203.     mCurState = kStatePlaying;
  204.  
  205.     return true;
  206. }
  207.  
  208. bool VDAudioOutputWaveOutW32::Stop() {
  209.     if (mCurState == kStateSilent) return true;
  210.  
  211.     if (mCurState >= kStateOpened) {
  212.         if (MMSYSERR_NOERROR != waveOutReset(mhWaveOut))
  213.             return false;
  214.  
  215.         mCurState = kStateOpened;
  216.  
  217.         CheckBuffers();
  218.     }
  219.  
  220.     return true;
  221. }
  222.  
  223. bool VDAudioOutputWaveOutW32::CheckBuffers() {
  224.     if (mCurState == kStateSilent) return true;
  225.  
  226.     if (!mBlocksPending)
  227.         return false;
  228.  
  229.     WAVEHDR& hdr = mHeaders[mBlockHead];
  230.  
  231.     if (!(hdr.dwFlags & WHDR_DONE))
  232.         return false;
  233.  
  234.     ++mBlockHead;
  235.     if (mBlockHead >= mBlockCount)
  236.         mBlockHead = 0;
  237.     --mBlocksPending;
  238.     VDASSERT(mBlocksPending >= 0);
  239.     return true;
  240. }
  241.  
  242. bool VDAudioOutputWaveOutW32::WaitBuffers(uint32 timeout) {
  243.     if (mCurState == kStateSilent) return true;
  244.  
  245.     if (mhWaveOut && timeout) {
  246.         for(;;) {
  247.             if (WAIT_OBJECT_0 != WaitForSingleObject(mhWaveEvent, timeout))
  248.                 return false;
  249.  
  250.             if (CheckBuffers())
  251.                 return true;
  252.         }
  253.     }
  254.  
  255.     return CheckBuffers();
  256. }
  257.  
  258. uint32 VDAudioOutputWaveOutW32::GetAvailSpace() {
  259.     CheckBuffers();
  260.     return (mBlockCount - mBlocksPending) * mBlockSize - mBlockWriteOffset;
  261. }
  262.  
  263. bool VDAudioOutputWaveOutW32::Write(const void *data, uint32 len) {
  264.     if (mCurState == kStateSilent)
  265.         return true;
  266.  
  267.     CheckBuffers();
  268.  
  269.     while(len) {
  270.         if (mBlocksPending >= mBlockCount) {
  271.             if (mCurState == kStateOpened) {
  272.                 if (!Start())
  273.                     return false;
  274.             }
  275.  
  276.             if (!WaitBuffers(0)) {
  277.                 if (!WaitBuffers(INFINITE)) {
  278.                     return false;
  279.                 }
  280.                 continue;
  281.             }
  282.             break;
  283.         }
  284.  
  285.         WAVEHDR& hdr = mHeaders[mBlockTail];
  286.  
  287.         uint32 tc = mBlockSize - mBlockWriteOffset;
  288.         if (tc > len)
  289.             tc = len;
  290.  
  291.         if (tc) {
  292.             if (data) {
  293.                 memcpy((char *)hdr.lpData + mBlockWriteOffset, data, tc);
  294.                 data = (const char *)data + tc;
  295.             } else
  296.                 memset((char *)hdr.lpData + mBlockWriteOffset, 0, tc);
  297.  
  298.             mBlockWriteOffset += tc;
  299.             len -= tc;
  300.         }
  301.  
  302.         if (mBlockWriteOffset >= mBlockSize) {
  303.             if (!Flush())
  304.                 return false;
  305.         }
  306.     }
  307.  
  308.     return true;
  309. }
  310.  
  311. bool VDAudioOutputWaveOutW32::Flush() {
  312.     if (mCurState == kStateOpened) {
  313.         if (!Start())
  314.             return false;
  315.     }
  316.  
  317.     if (mBlockWriteOffset <= 0)
  318.         return true;
  319.  
  320.     WAVEHDR& hdr = mHeaders[mBlockTail];
  321.  
  322.     hdr.dwBufferLength = mBlockWriteOffset;
  323.     hdr.dwFlags &= ~WHDR_DONE;
  324.     MMRESULT res = waveOutWrite(mhWaveOut, &hdr, sizeof hdr);
  325.     mBlockWriteOffset = 0;
  326.  
  327.     if (res != MMSYSERR_NOERROR)
  328.         return false;
  329.  
  330.     ++mBlockTail;
  331.     if (mBlockTail >= mBlockCount)
  332.         mBlockTail = 0;
  333.     ++mBlocksPending;
  334.     VDASSERT(mBlocksPending <= mBlockCount);
  335.     return true;
  336. }
  337.  
  338. bool VDAudioOutputWaveOutW32::Finalize(uint32 timeout) {
  339.     if (mCurState == kStateSilent) return true;
  340.  
  341.     Flush();
  342.  
  343.     while(CheckBuffers(), mBlocksPending) {
  344.         if (WAIT_OBJECT_0 != WaitForSingleObject(mhWaveEvent, timeout))
  345.             return false;
  346.     }
  347.  
  348.     return true;
  349. }
  350.  
  351. sint32 VDAudioOutputWaveOutW32::GetPosition() {
  352.     MMTIME mmtime;
  353.  
  354.     if (mCurState != kStatePlaying) return -1;
  355.  
  356.     mmtime.wType = TIME_SAMPLES;
  357.  
  358.     MMRESULT res;
  359.  
  360.     vdsynchronized(mcsWaveDevice) {
  361.         res = waveOutGetPosition(mhWaveOut, &mmtime, sizeof mmtime);
  362.     }
  363.  
  364.     if (MMSYSERR_NOERROR != res)
  365.         return -1;
  366.  
  367.     switch(mmtime.wType) {
  368.     case TIME_BYTES:
  369.         return MulDiv(mmtime.u.cb, 1000, mAvgBytesPerSec);
  370.     case TIME_MS:
  371.         return mmtime.u.ms;
  372.     case TIME_SAMPLES:
  373.         return MulDiv(mmtime.u.sample, 1000, mSamplesPerSec);
  374.     }
  375.  
  376.     return -1;
  377. }
  378.  
  379. bool VDAudioOutputWaveOutW32::IsFrozen() {
  380.     if (mCurState != kStatePlaying)
  381.         return true;
  382.  
  383.     CheckBuffers();
  384.  
  385.     return !mBlocksPending;
  386. }
  387.  
  388. ///////////////////////////////////////////////////////////////////////////
  389.  
  390. class VDAudioOutputDirectSoundW32 : public IVDAudioOutput {
  391. public:
  392.     VDAudioOutputDirectSoundW32();
  393.     ~VDAudioOutputDirectSoundW32();
  394.  
  395.     bool    Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf);
  396.     void    Shutdown();
  397.     void    GoSilent();
  398.  
  399.     bool    IsSilent();
  400.     bool    IsFrozen();
  401.     uint32    GetAvailSpace();
  402.     sint32    GetPosition();
  403.  
  404.     bool    Start();
  405.     bool    Stop();
  406.     bool    Flush();
  407.  
  408.     bool    Write(const void *data, uint32 len);
  409.     bool    Finalize(uint32 timeout);
  410.  
  411. private:
  412.     bool    Init2(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf);
  413.  
  414.     static ATOM sWndClass;
  415.  
  416.     HWND                mhwnd;
  417.     HMODULE                mhmodDS;
  418.     IDirectSound8        *mpDS8;
  419.     IDirectSoundBuffer8    *mpDSBuffer;
  420.  
  421.     uint32    mBufferSize;
  422.     uint32    mTailCursor;
  423.  
  424.     enum InitState {
  425.         kStateNone        = 0,
  426.         kStateOpened    = 1,
  427.         kStatePlaying    = 2,
  428.         kStateSilent    = 10,
  429.     } mCurState;
  430. };
  431.  
  432. ATOM VDAudioOutputDirectSoundW32::sWndClass;
  433.  
  434. IVDAudioOutput *VDCreateAudioOutputDirectSoundW32() {
  435.     return new VDAudioOutputDirectSoundW32;
  436. }
  437.  
  438. VDAudioOutputDirectSoundW32::VDAudioOutputDirectSoundW32()
  439.     : mhwnd(NULL)
  440.     , mhmodDS(NULL)
  441.     , mpDS8(NULL)
  442.     , mpDSBuffer(NULL)
  443. {
  444. }
  445.  
  446. VDAudioOutputDirectSoundW32::~VDAudioOutputDirectSoundW32() {
  447. }
  448.  
  449. bool VDAudioOutputDirectSoundW32::Init(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf) {
  450.     if (!Init2(bufsize, bufcount, wf)) {
  451.         Shutdown();
  452.         return false;
  453.     }
  454.     return true;
  455. }
  456.  
  457. bool VDAudioOutputDirectSoundW32::Init2(uint32 bufsize, uint32 bufcount, const tWAVEFORMATEX *wf) {
  458.     mBufferSize = bufsize * bufcount;
  459.  
  460.     // attempt to load DirectSound library
  461.     mhmodDS = LoadLibraryA("dsound");
  462.     if (!mhmodDS)
  463.         return false;
  464.  
  465.     typedef HRESULT (WINAPI *tpDirectSoundCreate8)(LPCGUID, LPDIRECTSOUND8 *, LPUNKNOWN);
  466.     tpDirectSoundCreate8 pDirectSoundCreate8 = (tpDirectSoundCreate8)GetProcAddress(mhmodDS, "DirectSoundCreate8");
  467.     if (!pDirectSoundCreate8) {
  468.         VDDEBUG("VDAudioOutputDirectSound: Cannot find DirectSoundCreate8 entry point!\n");
  469.         return false;
  470.     }
  471.  
  472.     // attempt to create DirectSound object
  473.     HRESULT hr = pDirectSoundCreate8(NULL, &mpDS8, NULL);
  474.     if (FAILED(hr)) {
  475.         VDDEBUG("VDAudioOutputDirectSound: Failed to create DirectSound object! hr=%08x\n", hr);
  476.         return false;
  477.     }
  478.  
  479.     // register window class
  480.     if (!sWndClass) {
  481.         WNDCLASS wc = {0};
  482.         wc.lpfnWndProc = DefWindowProc;
  483.         wc.lpszClassName = "VirtualDub DirectSound window";
  484.         wc.hInstance = g_hInst;
  485.         sWndClass = RegisterClass(&wc);
  486.         if (!sWndClass)
  487.             return false;
  488.     }
  489.  
  490.     // create window
  491.     mhwnd = CreateWindowA((LPCTSTR)sWndClass, "", WS_POPUP, 0, 0, 0, 0, NULL, NULL, g_hInst, NULL);
  492.     if (!mhwnd) {
  493.         VDDEBUG("VDAudioOutputDirectSound: Failed to create window!\n");
  494.         return false;
  495.     }
  496.  
  497.     // Set cooperative level.
  498.     //
  499.     // From microsoft.public.win32.programmer.directx.audio, by an SDE on the Windows AV team:
  500.     //
  501.     // "I can't speak for all DirectX components but DirectSound does not
  502.     //  subclass the window procedure.  It simply uses the window handle to
  503.     //  determine (every 1/2 second, in a seperate thread) if the window that
  504.     //  corresponds to the handle has the focus (Actually, it is slightly more
  505.     //  complicated than that, but that is close enough for this discussion). 
  506.     //  You can feel free to use the desktop window or console window for the
  507.     //  window handle if you are going to create GLOBAL_FOCUS buffers."
  508.     //
  509.     // Alright, you guys said we could do it!
  510.     //
  511.     hr = mpDS8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY);
  512.     if (FAILED(hr)) {
  513.         VDDEBUG("VDAudioOutputDirectSound: Failed to set cooperative level! hr=%08x\n", hr);
  514.         return false;
  515.     }
  516.  
  517.     // create looping secondary buffer
  518.     DSBUFFERDESC dsd={sizeof(DSBUFFERDESC)};
  519.     dsd.dwFlags            = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
  520.     dsd.dwBufferBytes    = bufsize * bufcount;
  521.     dsd.lpwfxFormat        = (WAVEFORMATEX *)wf;
  522.     dsd.guid3DAlgorithm    = DS3DALG_DEFAULT;
  523.  
  524.     IDirectSoundBuffer *pDSB;
  525.     hr = mpDS8->CreateSoundBuffer(&dsd, &pDSB, NULL);
  526.     if (FAILED(hr)) {
  527.         VDDEBUG("VDAudioOutputDirectSound: Failed to create secondary buffer! hr=%08x\n", hr);
  528.         return false;
  529.     }
  530.  
  531.     // query to IDirectSoundBuffer8
  532.     hr = pDSB->QueryInterface(IID_IDirectSoundBuffer8, (void **)&mpDSBuffer);
  533.     pDSB->Release();
  534.     if (FAILED(hr)) {
  535.         VDDEBUG("VDAudioOutputDirectSound: Failed to obtain IDirectSoundBuffer8 interface! hr=%08x\n", hr);
  536.         return false;
  537.     }
  538.  
  539.     // all done!
  540.     mTailCursor = 0;
  541.     return true;
  542. }
  543.  
  544. void VDAudioOutputDirectSoundW32::Shutdown() {
  545.     if (mpDSBuffer) {
  546.         mpDSBuffer->Release();
  547.         mpDSBuffer = NULL;
  548.     }
  549.  
  550.     if (mpDS8) {
  551.         mpDS8->Release();
  552.         mpDS8 = NULL;
  553.     }
  554.  
  555.     if (mhmodDS) {
  556.         FreeLibrary(mhmodDS);
  557.         mhmodDS = NULL;
  558.     }
  559.  
  560.     if (mhwnd) {
  561.         DestroyWindow(mhwnd);
  562.         mhwnd = NULL;
  563.     }
  564. }
  565.  
  566. void VDAudioOutputDirectSoundW32::GoSilent() {
  567. }
  568.  
  569. bool VDAudioOutputDirectSoundW32::IsSilent() {
  570.     return !mpDSBuffer;
  571. }
  572.  
  573. bool VDAudioOutputDirectSoundW32::IsFrozen() {
  574.     return false;
  575. }
  576.  
  577. uint32 VDAudioOutputDirectSoundW32::GetAvailSpace() {
  578.     DWORD playCursor, writeCursor;
  579.     HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, &writeCursor);
  580.  
  581.     sint32 space = playCursor - mTailCursor;
  582.     if (space < 0)
  583.         space += mBufferSize;
  584.  
  585.     return (uint32)space;
  586. }
  587.  
  588. sint32 VDAudioOutputDirectSoundW32::GetPosition() {
  589.     DWORD playCursor;
  590.     HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, NULL);
  591.  
  592.     return playCursor;
  593. }
  594.  
  595. bool VDAudioOutputDirectSoundW32::Start() {
  596.     if (!mpDSBuffer)
  597.         return true;
  598.  
  599.     HRESULT hr = mpDSBuffer->Play(0, 0, DSBPLAY_LOOPING);
  600.  
  601.     return SUCCEEDED(hr);
  602. }
  603.  
  604. bool VDAudioOutputDirectSoundW32::Stop() {
  605.     if (!mpDSBuffer)
  606.         return true;
  607.  
  608.     HRESULT hr = mpDSBuffer->Stop();
  609.     return SUCCEEDED(hr);
  610. }
  611.  
  612. bool VDAudioOutputDirectSoundW32::Flush() {
  613.     return true;
  614. }
  615.  
  616. bool VDAudioOutputDirectSoundW32::Write(const void *data, uint32 len) {
  617.     if (!mpDSBuffer)
  618.         return true;
  619.  
  620.     while(len > 0) {
  621.         DWORD playCursor, writeCursor;
  622.         HRESULT hr = mpDSBuffer->GetCurrentPosition(&playCursor, &writeCursor);
  623.         if (FAILED(hr)) {
  624.             return false;
  625.         }
  626.  
  627.         sint32 tc = (sint32)playCursor - mTailCursor;
  628.         if (tc < 0)
  629.             tc += mBufferSize;
  630.  
  631.         if (!tc) {
  632.             ::Sleep(1);
  633.             continue;
  634.         }
  635.  
  636.         if ((uint32)tc > len)
  637.             tc = len;
  638.  
  639.         LPVOID p1, p2;
  640.         DWORD tc1, tc2;
  641.         hr = mpDSBuffer->Lock(mTailCursor, tc, &p1, &tc1, &p2, &tc2, 0);
  642.         if (FAILED(hr))
  643.             return false;
  644.  
  645.         memcpy(p1, data, tc1);
  646.         data = (char *)data + tc1;
  647.         memcpy(p2, data, tc2);
  648.         data = (char *)data + tc2;
  649.  
  650.         mpDSBuffer->Unlock(p1, tc1, p2, tc2);
  651.  
  652.         len -= tc;
  653.  
  654.         mTailCursor += tc;
  655.         if (mTailCursor >= mBufferSize)
  656.             mTailCursor -= mBufferSize;
  657.     }
  658.  
  659.     return true;
  660. }
  661.  
  662. bool VDAudioOutputDirectSoundW32::Finalize(uint32 timeout) {
  663.     return true;
  664. }
  665.